/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package dropclient;

import com.progin.drop.core.Message;
import com.progin.drop.core.MessageException;
import com.progin.drop.core.MessageOperator;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.TimerTask;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author HP
 */
public class SyncTask extends TimerTask {
    private ArrayList<FileDescription> mAddedFiles,  mDownloadedFiles , mUploadedFiles , mDeletedFiles , mServerFiles , mClientFiles;
    private Socket mSock;
    private File mActiveDirectory;
    private boolean first = true;

    public SyncTask(String path) {
        mActiveDirectory = new File(path);
        mDownloadedFiles = new ArrayList<FileDescription>();
        mUploadedFiles = new ArrayList<FileDescription>();
        mDeletedFiles = new ArrayList<FileDescription>();
        mServerFiles = new ArrayList<FileDescription>();
        mClientFiles = new ArrayList<FileDescription>();
        mAddedFiles = new ArrayList<FileDescription>();
    }

    private void getServerFiles() throws UnknownHostException, IOException {
        mServerFiles.clear();
        mSock = new Socket(DropClientApp.getApplication().serverAddress , 2000);

        Message m = new Message(Message.MESSAGE_LIST_FILES , null);
        m.getmHeaders().add(DropClientApp.getApplication().user_id);
        MessageOperator mo = new MessageOperator(m);
        try {
            mo.send(mSock);
            m = mo.receive(mSock);
            //System.out.println(m);
            parseMessage(m);
        } catch (MessageException ex) {
            Logger.getLogger(MessageComm.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
        }
    }

    private void getAddedFiles() {
        mAddedFiles.clear();

        File[] files = mActiveDirectory.listFiles();
        for (File f : files) {
            if (f.isFile()) {
                boolean found = false;
                for (FileDescription fd : DropClientApp.getApplication().getCurrentFiles()) {
                    if (f.getName().equals(fd.getFilename())) {
                        found = true;
                    }
                }
                if (!found) {
                    FileDescription fd = new FileDescription(f);
                    mAddedFiles.add(fd);
                    //fd.setDeleted(true);
                    System.out.println("ADDED : " + fd.getFilename());
                }
            }
        }
    }

    private void getDeletedFiles() {
        mDeletedFiles.clear();

        File[] files = mActiveDirectory.listFiles();
        for (FileDescription fd : DropClientApp.getApplication().getCurrentFiles()) {
            boolean found = false;
            for (File f : files) {
                if (f.isFile()) {
                    if (f.getName().equals(fd.getFilename())) {
                        found = true;
                    }
                }
            }
            if (!found) {
                mDeletedFiles.add(fd);
                fd.setDeleted(true);
                System.out.println("DELETED : " + fd.getFilename());
            }
        }
    }

    private void getClientFiles(){
        mClientFiles.clear();

        File[] files = mActiveDirectory.listFiles();
        for (File f : files) {
            if (f.isFile()) {
                FileDescription fd = new FileDescription(f);
                mClientFiles.add(fd);
            }
        }
    }

    private void parseMessage(Message msg) {
        for (String s : msg.getmBodies()) {
            mServerFiles.add(new FileDescription(s));
        }
    }

    private void debug(ArrayList<FileDescription> coll) {
        for (FileDescription fd : coll) {
            System.out.print(fd.getFilename() + " ");
        }
        System.out.println("");
    }

    
    private void compareFilesClientServer(){
        boolean found;
        mDownloadedFiles.clear();
        mUploadedFiles.clear();
        
        for (FileDescription serverFile : mServerFiles) {
            FileDescription temp;
            if (!serverFile.getIsDeleted()) {
                found = false;
                temp = null;
                for (FileDescription clientFile : mClientFiles) {
                    if (serverFile.getFilename().equals(clientFile.getFilename())) {
                        found = true;
                        temp = clientFile;
                    }
                }
                if (found) {
                    //File server = new File(DropClientApp.getApplication().curpath + "/" + serverFile.getFilename());
                    File client = new File(DropClientApp.getApplication().curpath + "/" + temp.getFilename());

                    if (!serverFile.getChecksum().equals(Util.getFileChecksum(client))) {
                        int comp = Long.valueOf(serverFile.getDatemodified()).compareTo(Long.valueOf(temp.getDatemodified()));
                        if (comp < 0) {
                            //kalo newer di client, add file ke mUploadedFiles, delete file di server
                            mUploadedFiles.add(temp);
                            //mServerFiles.remove(i);
                        } else if (comp > 0) {
                            //kalo newer di server, add file ke mDownloadedFiles, delete file di client
                            mDownloadedFiles.add(serverFile);
                            //mClientFiles.remove(j);
                        }
                    }
                }
                else {
                    mDownloadedFiles.add(serverFile);
                }
            }
            else {
                found = false;
                temp = null;
                if (!first) {
                    for (FileDescription addedFile : mAddedFiles) {
                        if (serverFile.getFilename().equals(addedFile.getFilename())) {
                            found = true;
                            temp = addedFile;
                        }
                    }
                }

                if (!found) {
                    File client = new File(DropClientApp.getApplication().curpath + "/" + serverFile.getFilename());
                    if (client.exists())
                        client.delete();
                }
                else
                    mUploadedFiles.add(temp);
            }
        }

        for (FileDescription clientFile : mClientFiles) {
            found = false;
            for (FileDescription serverFile : mServerFiles) {
                if (serverFile.getFilename().equals(clientFile.getFilename())) {
                    found = true;
                }
            }
            if (!found) {
                mUploadedFiles.add(clientFile);
            }
        }

        ArrayList<FileDescription> temp = new ArrayList<FileDescription>();

        for (FileDescription downloadedFile : mDownloadedFiles) {
            found = false;
            for (FileDescription deletedFile : mDeletedFiles)
                if (deletedFile.getFilename().equals(downloadedFile.getFilename()))
                    found = true;
            if (!found)
                temp.add(downloadedFile);
        }
        mDownloadedFiles = temp;
    }

    private void uploadFiles() throws UnknownHostException, IOException{
        MessageOperator mo = new MessageOperator();
        for (FileDescription fd : mUploadedFiles) {
            mSock = new Socket(DropClientApp.getApplication().serverAddress , 2000);
            try {
                Message m = new Message(Message.MESSAGE_SEND_FILE , new File(DropClientApp.getApplication().curpath + "/" + fd.getFilename()));
                m.getmHeaders().add(DropClientApp.getApplication().user_id);
                m.getmBodies().add(fd.getFilename());
                //m.setByteStream(readFile(new File(DropClientApp.getApplication().curpath + "/" + fd.getFilename())));

                mo.setTargetMessage(m);
                mo.send(mSock);

                //m = mo.receive(mSock);

                System.out.println("Upload : " + m);
            } catch (MessageException ex) {
                Logger.getLogger(MessageComm.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
            }
        }
    }

    private void downloadFiles() throws UnknownHostException, IOException{
        MessageOperator mo = new MessageOperator();
        for (FileDescription fd : mDownloadedFiles) {
            mSock = new Socket(DropClientApp.getApplication().serverAddress , 2000);

            boolean skipped = false;
            /*for (FileDescription fd2 : mDeletedFiles) {
                if (fd.getFilename().equals(fd2.getFilename()))
                    skipped = true;
            }*/
            if (!skipped) {
                try {
                    Message m = new Message(Message.MESSAGE_REQUEST_FILE , null);
                    m.getmHeaders().add(DropClientApp.getApplication().user_id);
                    m.getmBodies().add(fd.getFilename());
                    mo.setTargetMessage(m);
                    System.out.println("Download : " + m);
                    mo.send(mSock);
                    m = mo.receive(mSock);
                    File targetFile = new File(DropClientApp.getApplication().curpath + "/" + fd.getFilename());
                    mo.setHandler(new com.progin.drop.server.StreamHandler(targetFile));
                    mo.loadAttachment(mSock);
                    

                    //System.out.println("Download : " + m);
                    /*if (m.getmHeaders().get(2).equals("0") && Util.convertToHex(Util.getHash(m.getByteStream())).equals(fd.getChecksum())) {
                        writeFile(m.getBody(0), m.getByteStream());
                    }
                    else {
                    }*/
                } catch (MessageException ex) {
                    Logger.getLogger(MessageComm.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
                }
            }
        }
    }

    private void deleteFiles() throws UnknownHostException, IOException {
        MessageOperator mo = new MessageOperator();
        for (FileDescription fd : mDeletedFiles) {
            mSock = new Socket(DropClientApp.getApplication().serverAddress , 2000);
            try {
                Message m = new Message(Message.MESSAGE_REQUEST_FILE , null);
                m.getmHeaders().add(DropClientApp.getApplication().user_id);
                m.getmBodies().add(fd.getFilename());
                m.getmBodies().add("delete");
                mo.setTargetMessage(m);
                mo.send(mSock);
                System.out.println("Delete : " + m);

                m = mo.receive(mSock);
                System.out.println("Delete 2 : " + m);
            } catch (MessageException ex) {
                Logger.getLogger(MessageComm.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
            }
        }
    }

    private void writeFile(String filename , byte[] stream) {
        try {
            File targetFile = new File(DropClientApp.getApplication().curpath + "/" + filename);
            FileOutputStream out;

            if (targetFile.createNewFile()) {
            }
            else {
            }

            if (stream != null) {
                out = new FileOutputStream(targetFile);
                out.write(stream);
                out.close();
            }

        } catch (IOException ex) {
            Logger.getLogger(Handler.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
        }
    }

    private byte[] readFile(File file) {
        if (file.exists() && file.isFile()) {
            FileInputStream fis;
            try {
                fis = new FileInputStream(file);
                byte[] b = new byte[(int)file.length()];
                fis.read(b);
                fis.close();

                return b;
            } catch (IOException ex) {
                Logger.getLogger(Util.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
                return null;
            }
        }
        else
            return null;
    }

    @Override
    public void run() {
        System.out.println("TIMED SYNC");

        try {
            getServerFiles();
            getDeletedFiles();
            getAddedFiles();
            getClientFiles();
            DropClientApp.getApplication().setCurrentFiles(mClientFiles);

            compareFilesClientServer();

            System.out.print("server : ");debug(mServerFiles);
            System.out.print("client : ");debug(mClientFiles);
            System.out.print("downloads : ");debug(mDownloadedFiles);
            System.out.print("uploads : ");debug(mUploadedFiles);
            System.out.print("deletes : ");debug(mDeletedFiles);
            System.out.print("added : ");debug(mAddedFiles);

            first = false;
            deleteFiles();
            downloadFiles();
            uploadFiles();
            // TODO add your handling code here:
            /*MessageComm comm = new MessageComm(new Socket(DropClientApp.getApplication().serverAddress, 2000));
            comm.synchronize();*/
        } catch (UnknownHostException ex) {
            Logger.getLogger(DropClientView.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(DropClientView.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

}
